aboutsummaryrefslogtreecommitdiff
path: root/internal/calendar/precedence_ef_repro_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/calendar/precedence_ef_repro_test.go')
-rw-r--r--internal/calendar/precedence_ef_repro_test.go160
1 files changed, 160 insertions, 0 deletions
diff --git a/internal/calendar/precedence_ef_repro_test.go b/internal/calendar/precedence_ef_repro_test.go
new file mode 100644
index 0000000..11fd6ca
--- /dev/null
+++ b/internal/calendar/precedence_ef_repro_test.go
@@ -0,0 +1,160 @@
+package calendar_test
+
+// Reproduction tests for three named EF precedence defects (colitur's
+// differential/oracle audit against lectio's ~66-line EF precedence
+// approximation, internal/calendar/precedence_ef.go). Each test fails before
+// its fix and passes after -- see the report for the exact pre-fix failure
+// message. These exercise the REAL tridentine sanctoral data
+// (caldata.Tridentine()) through the public Compute entry point, since the
+// bugs are about how real fixed-date feasts interact with the temporal
+// calendar, not about precedenceEF's arithmetic in isolation (that is
+// covered separately, package-internal, in precedence_ef_test.go).
+
+import (
+ "testing"
+ "time"
+
+ "github.com/lukaszkasprzak/lectio/internal/caldata"
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+)
+
+func efCompute(date string) calendar.LiturgicalDay {
+ sel := calendar.DefaultSelection()
+ sel.Form = "old"
+ d, _ := time.Parse("2006-01-02", date)
+ return calendar.Compute(d.UTC(), sel, []calendar.Layer{caldata.Tridentine()})
+}
+
+// TestJosephYieldsToSundayOfLent: RG 91 entry 6 (Sundays of Lent, I class)
+// vs entry 11 (I-class feasts of the universal Church not above) -- the
+// Sunday holds; St Joseph (19 March) transfers to the next free day (RG 95,
+// RG 96), landing on the 20th. Before the fix (defect 4: Sunday ranks), Lent
+// Sundays were wrongly II class, so Joseph (I class) won outright every time
+// this collision occurred.
+func TestJosephYieldsToSundayOfLent(t *testing.T) {
+ for _, year := range []string{"2006", "2017", "2023", "2028", "2034", "2045"} {
+ sunday := efCompute(year + "-03-19")
+ if sunday.Observed.Slug == "joseph-spouse-of-the-bl-virgin-mary" {
+ t.Errorf("%s-03-19 observed = %q want the Lent Sunday (RG 91 entry 6 beats entry 11)", year, sunday.Observed.Slug)
+ }
+ next := efCompute(year + "-03-20")
+ if next.Observed.Slug != "joseph-spouse-of-the-bl-virgin-mary" {
+ t.Errorf("%s-03-20 observed = %q want joseph-spouse-of-the-bl-virgin-mary (transferred one day, RG 96)", year, next.Observed.Slug)
+ }
+ }
+}
+
+// TestTransferSkipsBothIAndIIClass: RG 96 -- an impeded I-class feast
+// transfers to the next day that is NOT ITSELF I OR II CLASS (not merely "not
+// I class"). 2011: the Sacred Heart (Friday after the Corpus Christi octave)
+// falls on 1 July and impedes the Precious Blood (also 1 July, fixed). 2
+// July is the Visitation (II class, fixed); 3 July is an ordinary II-class
+// Sunday. Both must be skipped; the Precious Blood lands on 4 July, and the
+// Visitation is observed, undisplaced, on its own day.
+func TestTransferSkipsBothIAndIIClass(t *testing.T) {
+ if got := efCompute("2011-07-01").Observed.Slug; got != "ef-sacred-heart" {
+ t.Fatalf("2011-07-01 observed = %q want ef-sacred-heart", got)
+ }
+ if got := efCompute("2011-07-02").Observed.Slug; got != "visitation-of-the-blessed-virgin-mary" {
+ t.Errorf("2011-07-02 observed = %q want visitation-of-the-blessed-virgin-mary (RG 96: the Precious Blood must skip past it, not displace it)", got)
+ }
+ if got := efCompute("2011-07-03").Observed.Slug; got != "ef-time-after-pentecost-sunday-3" {
+ t.Errorf("2011-07-03 observed = %q want ef-time-after-pentecost-sunday-3 (an ordinary II-class Sunday also blocks a I-class transfer)", got)
+ }
+ if got := efCompute("2011-07-04").Observed.Slug; got != "precious-blood-of-our-lord-jesus-christ" {
+ t.Errorf("2011-07-04 observed = %q want precious-blood-of-our-lord-jesus-christ (RG 96: first day that is neither I nor II class)", got)
+ }
+}
+
+// TestMatthewBeatsSeptemberEmberWednesday: RG 91 -- St Matthew (21 September,
+// II class) and the September Ember Wednesday are both II class; the table
+// decides the tie. Entry 16 (II-class feasts of the universal Church) sits
+// ABOVE entry 18 (II-class ferias, including the Ember days) -- the feast
+// wins, the Ember feria is only commemorated.
+func TestMatthewBeatsSeptemberEmberWednesday(t *testing.T) {
+ // 21 September falls on the September Ember Wednesday whenever the third
+ // Sunday of September is the 18th -- 2016 and 2022 both qualify.
+ for _, date := range []string{"2016-09-21", "2022-09-21"} {
+ day := efCompute(date)
+ if day.Observed.Slug != "matthew" {
+ t.Errorf("%s observed = %q want matthew (RG 91 entry 16 beats entry 18)", date, day.Observed.Slug)
+ }
+ }
+}
+
+// TestImmaculateConceptionBeatsAdventSunday: found, not named among the
+// seven, while verifying defect 4 (Sunday ranks) against the real sanctoral
+// data. RG 91 entry 4 (Immaculate Conception, Assumption BVM) sits ABOVE
+// entry 6 (Sundays of Advent/Lent/Passiontide) -- unlike an ORDINARY
+// I-class feast (entry 11, e.g. St Joseph), the Immaculate Conception
+// (8 December) is not impeded by the Advent Sunday it falls on at all. Before
+// defect 4's own fix, Advent Sundays were wrongly II class, so this was
+// accidentally right (I class beats II class outright); once Sundays became
+// I class the tie mattered for the first time.
+func TestImmaculateConceptionBeatsAdventSunday(t *testing.T) {
+ for _, year := range []string{"2013", "2019", "2024"} {
+ day := efCompute(year + "-12-08")
+ if day.Observed.Slug != "immaculate-conception-of-the-blessed-virgin-mary" {
+ t.Errorf("%s-12-08 observed = %q want immaculate-conception-of-the-blessed-virgin-mary (RG 91 entry 4 beats entry 6)", year, day.Observed.Slug)
+ }
+ }
+}
+
+// TestVigilOfChristmasSurvivesAdventSunday: found, not named among the
+// seven, while verifying defect 4 against the real sanctoral data -- and
+// worse than a wrong winner. RG 91 entry 5 (Vigil & Octave day of the
+// Nativity) also sits above entry 6, so the Vigil of Christmas (24 December)
+// is not impeded by falling on Advent IV either. Without this, defect 4
+// alone would have made the Advent Sunday win a genuine tie, sending the
+// Vigil into transferIfImpededEF's forward walk -- which has no way to
+// re-place a transfer that crosses the Dec31/Jan1 boundary (celebrationDate
+// re-resolves a fixed date using the year of whatever day is being queried,
+// so a walk landing in the following January can never match the query that
+// produced it). The Vigil did not move to the wrong day; it vanished for the
+// whole year.
+func TestVigilOfChristmasSurvivesAdventSunday(t *testing.T) {
+ for _, year := range []string{"2006", "2017", "2023", "2028"} {
+ day := efCompute(year + "-12-24")
+ if day.Observed.Slug != "vigil-of-christmas" {
+ t.Errorf("%s-12-24 observed = %q want vigil-of-christmas (RG 91 entry 5 beats entry 6; must not vanish)", year, day.Observed.Slug)
+ }
+ }
+}
+
+// TestPurificationBeatsFebruarySunday: the fixture behind classOf's own
+// Purification decision (scripts/gen-sanctoral-ef.go), committed here rather
+// than left as an assertion only, because the deciding years (2 February on
+// a Sunday: 2014, 2020, 2025, 2031, 2042, 2048) fall entirely outside this
+// repo's own missalemeum oracle snapshot (sources/snapshot.tar.gz,
+// 2026-01-01 .. 2027-12-31), so nothing else in this repository can
+// reproduce the evidence classOf's decision rests on. (2036, sometimes
+// quoted alongside 2025/2031 as a third example: 2 February 2036 is in fact
+// a SATURDAY, `date -d 2036-02-02 +%A` -- checked here rather than repeated;
+// 2042 is the correct next instance after 2031.)
+//
+// RG 91 entry 14 ("Festa Domini II classis") sits above entry 15
+// ("Dominicae II classis"), which sits above entry 16 ("Festa II classis
+// Ecclesiae universae, quae non [sunt Domini]") -- entry 14 takes an
+// occurring Sunday's place outright, entry 16 is merely commemorated on
+// one. The Purification (2 February) matches entry 14's pattern, not entry
+// 16's, independently fetched live from missalemeum for five different
+// Sundays: 2014-02-02, 2020-02-02, 2025-02-02, 2031-02-02, 2042-02-02 (all
+// "id":"sancti:02-02:2:w", "commemorations":[]). Control:
+// nativity-of-the-blessed-virgin-mary (8 September, an undisputed ordinary
+// Marian feast, entry 16's own pattern) on a Sunday -- 2019-09-08 -- shows
+// the SUNDAY observed, the feast commemorated instead: the opposite shape,
+// proving the Purification's own treatment is a deliberate pattern in the
+// oracle, not a gap. This is a decision AGAINST the calendarium's own title
+// ("IN PURIFICATIONE B. MARIAE VIRG.") and RG 120(b) (which files 2
+// February under the white-colour rule's "B. Mariae Virg." heading, kept
+// separate from 120(a)'s "Domini" heading) -- both point BVM; the
+// occurrence-behaviour evidence here points Domini. See classOf's own doc
+// comment for the fuller account; not re-argued here.
+func TestPurificationBeatsFebruarySunday(t *testing.T) {
+ for _, year := range []string{"2014", "2020", "2025", "2031", "2042"} {
+ day := efCompute(year + "-02-02")
+ if day.Observed.Slug != "purification-of-the-blessed-virgin-mary" {
+ t.Errorf("%s-02-02 observed = %q want purification-of-the-blessed-virgin-mary (RG 91 entry 14 beats entry 15)", year, day.Observed.Slug)
+ }
+ }
+}