package calendar_test import ( "strings" "testing" "time" "github.com/lukaszkasprzak/lectio/internal/caldata" "github.com/lukaszkasprzak/lectio/internal/calendar" ) func day(y int, m time.Month, d int) time.Time { return time.Date(y, m, d, 0, 0, 0, 0, time.UTC) } // TestEFCoverage checks the EF special days added for full coverage: the movable // vigils, the September/Advent Ember days, and the transfer of an impeded feast. // (2027: Easter is March 28.) func TestEFCoverage(t *testing.T) { sel := calendar.DefaultSelection() sel.Form = "old" layers := []calendar.Layer{caldata.Tridentine()} cases := []struct { date time.Time slug string rank calendar.Rank }{ {day(2027, time.September, 22), "ef-september-ember-wed", calendar.RankClass2}, {day(2027, time.December, 15), "ef-advent-ember-wed", calendar.RankClass2}, {day(2027, time.May, 5), "ef-ascension-vigil", calendar.RankClass2}, {day(2027, time.May, 15), "ef-pentecost-vigil", calendar.RankClass1}, {day(2027, time.June, 23), "vigil-of-the-nativity-of-st-john-the-baptist", calendar.RankClass2}, // Annunciation (Mar 25) is in Holy Week in 2027, so it is transferred to // the Monday after Low Sunday (Apr 5); Mar 25 itself keeps the Triduum. {day(2027, time.April, 5), "annunciation-of-the-blessed-virgin-mary", calendar.RankClass1}, } for _, c := range cases { got := calendar.Compute(c.date, sel, layers) if got.Observed.Slug != c.slug || got.Observed.Rank != c.rank { t.Errorf("%s: observed %q/%v, want %q/%v", c.date.Format("2006-01-02"), got.Observed.Slug, got.Observed.Rank, c.slug, c.rank) } } // The Annunciation must NOT also appear on its impeded fixed date. if got := calendar.Compute(day(2027, time.March, 25), sel, layers); strings.Contains(got.Observed.Slug, "annunciation") { t.Errorf("2027-03-25 (Holy Thursday) wrongly observes %q", got.Observed.Slug) } // All Souls: Nov 2 2025 is a Sunday, so it is transferred to Nov 3. if got := calendar.Compute(day(2025, time.November, 3), sel, layers); got.Observed.Slug != "commemoration-of-all-souls" { t.Errorf("2025-11-03: observed %q, want the transferred All Souls", got.Observed.Slug) } } // TestOFOrdinaryTimeFerialWeek guards the post-Pentecost Ordinary-Time ferial // week numbering, which was one too high (a weekday took Christ-the-King's week // instead of its preceding Sunday's). calapi confirms these canonical weeks. func TestOFOrdinaryTimeFerialWeek(t *testing.T) { sel := calendar.DefaultSelection() layers := []calendar.Layer{caldata.Universal()} for _, c := range []struct { date time.Time slug string }{ {day(2026, time.June, 8), "ordinary-10-mon"}, // calapi: 10th week {day(2026, time.June, 15), "ordinary-11-mon"}, // calapi: 11th week } { if got := calendar.Compute(c.date, sel, layers); got.Observed.Slug != c.slug { t.Errorf("%s: OT ferial slug %q, want %q", c.date.Format("2006-01-02"), got.Observed.Slug, c.slug) } } } // TestOFMovableMemorials checks Mary, Mother of the Church, the Immaculate Heart, // and the two-obligatory-memorials rule. func TestOFMovableMemorials(t *testing.T) { sel := calendar.DefaultSelection() // Ordinary Form layers := []calendar.Layer{caldata.Universal()} // Mary, Mother of the Church: Monday after Pentecost (2027: May 17), with its // proper readings (not the ferial). got := calendar.Compute(day(2027, time.May, 17), sel, layers) if got.Observed.Slug != "mary-mother-of-the-church" { t.Errorf("2027-05-17: observed %q, want mary-mother-of-the-church", got.Observed.Slug) } var first string for _, m := range got.Observed.Masses { for _, r := range m.Readings { if r.Part == "first" { first = r.Citation } } } if first != "Genesis 3:9-15,20" { t.Errorf("Mary MotC first reading = %q, want Genesis 3:9-15,20", first) } // Two obligatory memorials coincide (St Anthony of Padua + the Immaculate // Heart, 2026-06-13): both become optional and the ferial is observed. if got := calendar.Compute(day(2026, time.June, 13), sel, layers); got.Observed.Rank != calendar.RankFerial { t.Errorf("2026-06-13: observed %q/%v, want the ferial", got.Observed.Slug, got.Observed.Rank) } // But Mary, Mother of the Church keeps precedence over a coinciding memorial // (2028-06-05, with St Boniface). if got := calendar.Compute(day(2028, time.June, 5), sel, layers); got.Observed.Slug != "mary-mother-of-the-church" { t.Errorf("2028-06-05: observed %q, want mary-mother-of-the-church", got.Observed.Slug) } }