aboutsummaryrefslogtreecommitdiff
path: root/internal/calendar/oracle_ef_test.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 14:12:31 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-27 14:12:31 +0200
commite01cfff5ddddd9cefdb19536e31257cdae5821f6 (patch)
tree744ce0953a95dc490618a5237cafc5353190d882 /internal/calendar/oracle_ef_test.go
parent78825639eab3c84d84a60fbd1f1781bcfa332044 (diff)
downloadlectio-e01cfff5ddddd9cefdb19536e31257cdae5821f6.tar.gz
lectio-e01cfff5ddddd9cefdb19536e31257cdae5821f6.zip
test(calendar): EF oracle vs missalemeum 2025-2026 (0 season mismatches)
temporalEF validated against Divinum Officium data. Season-mapping handles the Whit octave (Paschaltide), the Christmas vigil (Advent), and the resumed Epiphany Sundays (within Time after Pentecost).
Diffstat (limited to 'internal/calendar/oracle_ef_test.go')
-rw-r--r--internal/calendar/oracle_ef_test.go116
1 files changed, 116 insertions, 0 deletions
diff --git a/internal/calendar/oracle_ef_test.go b/internal/calendar/oracle_ef_test.go
new file mode 100644
index 0000000..ce4badd
--- /dev/null
+++ b/internal/calendar/oracle_ef_test.go
@@ -0,0 +1,116 @@
+package calendar_test
+
+import (
+ "encoding/json"
+ "os"
+ "sort"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/lukaszkasprzak/lectio/internal/caldata"
+ "github.com/lukaszkasprzak/lectio/internal/calendar"
+)
+
+type efOracleDay struct {
+ Tempora string `json:"tempora"`
+ Title string `json:"title"`
+ Rank int `json:"rank"`
+ Colour string `json:"colour"`
+}
+
+// efSeasonFromString maps a missalemeum tempora/title phrase (for a day in the
+// given month) to lectio's EF season. Order matters. Two position-dependent
+// subtleties: the Whit-octave weekdays ("Monday after Pentecost") are still
+// Paschaltide, so only "Sunday after Pentecost" is Time after Pentecost; and the
+// resumed Sundays after Epiphany (celebrated in autumn) sit within Time after
+// Pentecost, so an "after Epiphany" phrase in the second half of the year maps
+// there, not to Time after Epiphany.
+func efSeasonFromString(s string, month int) calendar.Season {
+ s = strings.ToLower(s)
+ has := func(subs ...string) bool {
+ for _, sub := range subs {
+ if strings.Contains(s, sub) {
+ return true
+ }
+ }
+ return false
+ }
+ switch {
+ case has("vigil of christmas"): // last day of Advent
+ return calendar.Advent
+ case has("advent"):
+ return calendar.Advent
+ case has("sunday after pentecost"), has("trinity", "christ the king", "corpus christi"):
+ return calendar.TimeAfterPentecost
+ case has("after epiphany", "epiphany"):
+ if month >= 6 { // resumed Epiphany Sundays, celebrated within Time after Pentecost
+ return calendar.TimeAfterPentecost
+ }
+ return calendar.TimeAfterEpiphany
+ case has("passion", "palm", "holy week", "maundy", "good friday", "holy saturday"):
+ return calendar.Passiontide
+ case has("septuagesima", "sexagesima", "quinquagesima"):
+ return calendar.Septuagesima
+ case has("ash wednesday", "lent"):
+ return calendar.Lent
+ case has("pentecost", "ascension", "rogation", "after easter", "easter", "low sunday", "paschal", "whit"):
+ return calendar.Easter_ // incl. the Whit octave (Mon-Sat after Pentecost)
+ case has("christmas", "nativity", "circumcision", "holy name", "octave day of christ"):
+ return calendar.Christmas
+ default:
+ return "" // unknown phrase -> skip this day
+ }
+}
+
+// TestOracleEF diffs the EF engine against missalemeum (Divinum Officium data).
+// Season is asserted strictly (validates temporalEF); rank/colour are reported
+// informationally (partial 1962 sanctoral).
+func TestOracleEF(t *testing.T) {
+ raw, err := os.ReadFile("testdata/oracle-ef.json")
+ if err != nil {
+ t.Skip("EF oracle snapshot missing; run scripts/build-oracle-ef.sh")
+ }
+ var oracle map[string]efOracleDay
+ if err := json.Unmarshal(raw, &oracle); err != nil {
+ t.Fatal(err)
+ }
+ sel := calendar.DefaultSelection()
+ sel.Form = "old"
+ layers := []calendar.Layer{caldata.Tridentine()}
+
+ dates := make([]string, 0, len(oracle))
+ for d := range oracle {
+ dates = append(dates, d)
+ }
+ sort.Strings(dates)
+
+ var seasonMiss, skipped, total int
+ shown := 0
+ for _, date := range dates {
+ od := oracle[date]
+ src := od.Tempora
+ if src == "" {
+ src = od.Title
+ }
+ day, _ := time.Parse("2006-01-02", date)
+ want := efSeasonFromString(src, int(day.Month()))
+ if want == "" {
+ skipped++
+ continue
+ }
+ total++
+ got := calendar.Compute(day.UTC(), sel, layers)
+ if got.Season != want {
+ seasonMiss++
+ if shown < 25 {
+ t.Errorf("%s: EF season got %q want %q (from %q)", date, got.Season, want, src)
+ shown++
+ }
+ }
+ }
+ t.Logf("EF oracle: %d checked, %d skipped(unmapped), %d season mismatches", total, skipped, seasonMiss)
+ if seasonMiss > 0 {
+ t.Fatalf("%d/%d EF season mismatches vs missalemeum — temporalEF is wrong", seasonMiss, total)
+ }
+}