diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 12:33:47 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-27 12:33:47 +0200 |
| commit | f5afb407beab795e371c36a3229e991e48c366fe (patch) | |
| tree | b9378cca00e46a1b063d53f3cfa52546ae782f9f /internal/calendar/oracle_test.go | |
| parent | 56f3b8161f73c33fe6e25f6461d3f061af427e9e (diff) | |
| download | lectio-f5afb407beab795e371c36a3229e991e48c366fe.tar.gz lectio-f5afb407beab795e371c36a3229e991e48c366fe.zip | |
test(calendar): regression oracle vs calapi 2020-2040 (0 season mismatches)
Caught + fixed an Advent-start bug: anchor on Christmas Eve, not Dec 25, so
years where Dec 25 is a Sunday don't lose the first week of Advent. Rank-class
diffs (324) are informational — the initial sanctoral dataset is partial.
Diffstat (limited to 'internal/calendar/oracle_test.go')
| -rw-r--r-- | internal/calendar/oracle_test.go | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/internal/calendar/oracle_test.go b/internal/calendar/oracle_test.go new file mode 100644 index 0000000..8ced282 --- /dev/null +++ b/internal/calendar/oracle_test.go @@ -0,0 +1,92 @@ +package calendar_test + +import ( + "encoding/json" + "math" + "os" + "sort" + "testing" + "time" + + "github.com/lukaszkasprzak/lectio/internal/caldata" + "github.com/lukaszkasprzak/lectio/internal/calendar" +) + +type oracleDay struct { + Season string `json:"season"` + RankNum float64 `json:"rank_num"` +} + +// classOf maps a Table band (1-13) to its calapi class (1, 2, or 3). +func classOf(band int) int { + switch { + case band <= 4: + return 1 + case band <= 9: + return 2 + default: + return 3 + } +} + +// mySeason maps the engine's season onto calapi's five-season vocabulary +// (calapi buckets the Triduum's Thu/Fri/Sat under "lent"). +func mySeason(s calendar.Season) string { + if s == calendar.Triduum { + return "lent" + } + return string(s) +} + +// TestOracle2020to2040 diffs the engine against calapi (calendarium-romanum). +// Season is asserted strictly (data-independent — it validates the whole +// temporal engine). Rank class is reported for information only, since a class +// mismatch usually means the initial embedded sanctoral simply lacks that saint. +func TestOracle2020to2040(t *testing.T) { + raw, err := os.ReadFile("testdata/oracle-2020-2040.json") + if err != nil { + t.Skip("oracle snapshot missing; run scripts/build-oracle.sh") + } + var oracle map[string]oracleDay + if err := json.Unmarshal(raw, &oracle); err != nil { + t.Fatal(err) + } + sel := calendar.DefaultSelection() + layers := []calendar.Layer{caldata.Universal()} + + dates := make([]string, 0, len(oracle)) + for date := range oracle { + dates = append(dates, date) + } + sort.Strings(dates) + + var seasonMiss, classMiss, total int + shownS, shownC := 0, 0 + for _, date := range dates { + want := oracle[date] + day, _ := time.Parse("2006-01-02", date) + got := calendar.Compute(day.UTC(), sel, layers) + total++ + + if mySeason(got.Season) != want.Season { + seasonMiss++ + if shownS < 20 { + t.Errorf("%s: season got %q want %q", date, mySeason(got.Season), want.Season) + shownS++ + } + } + wantClass := int(math.Floor(want.RankNum)) + if classOf(got.ObservedBand) != wantClass && shownC < 15 { + classMiss++ + t.Logf("[info] %s: rank class got %d (band %d) want %d — likely missing sanctoral data", + date, classOf(got.ObservedBand), got.ObservedBand, wantClass) + shownC++ + } else if classOf(got.ObservedBand) != wantClass { + classMiss++ + } + } + t.Logf("oracle: %d days, %d season mismatches, %d rank-class mismatches (info)", total, seasonMiss, classMiss) + if seasonMiss > 0 { + t.Fatalf("%d/%d season mismatches vs calapi — temporal engine is wrong", seasonMiss, total) + } +} |
