diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/test_lectionary_of.ml | 119 |
1 files changed, 112 insertions, 7 deletions
diff --git a/test/test_lectionary_of.ml b/test/test_lectionary_of.ml index 6fb48a0..03838be 100644 --- a/test/test_lectionary_of.ml +++ b/test/test_lectionary_of.ml @@ -172,13 +172,14 @@ let sha256_of_file path = | hash :: _ -> hash | [] -> Alcotest.fail "sha256sum produced no output" -(* Pinned 2026-08-26 against the shipped file itself, independently - re-derived (`sha256sum data/of/lectionary.sexp`), the same "derive the - new value, don't transcribe it from generator stdout" discipline +(* Pinned 2026-08-26 (FIX 1, O-Antiphon/Christmas-season date-keyed review) + against the shipped file itself, independently re-derived + (`sha256sum data/of/lectionary.sexp`), the same "derive the new value, + don't transcribe it from generator stdout" discipline test_calendar_of_data.ml's own [test_sha256] already follows. *) let test_sha256_pinned () = Alcotest.(check string) "data/of/lectionary.sexp SHA-256" - "e7fd109bc30baf8a5531a614995e986959214424c80f4d444fa2b772538decde" (sha256_of_file lectionary_path) + "2277e3dc302e853d78b72608884f59eb67671bfdcca4f5e4c99358ee12811f89" (sha256_of_file lectionary_path) (* lectio's OWN of-lectionary.ini SHA-256, pinned inside data/of/ lectionary.sexp's own provenance header (tools/bootstrap_lectionary_of @@ -244,7 +245,7 @@ let test_missing_source_fails_loudly () = let test_load () = let l = real_lectionary () in - Alcotest.(check int) "754 entries" 754 (List.length (Lectionary.entries l)) + Alcotest.(check int) "770 entries" 770 (List.length (Lectionary.entries l)) (* ---- coverage: a full sample civil year, both directions, PINNED ------ *) @@ -357,6 +358,105 @@ let test_step2_sanctoral_wins () = (cs <> Option.get (Lectionary.find lectionary (Slug.of_string_exn "of-advent-1-monday"))) | _ -> Alcotest.fail "30 November 2026 should resolve readings via step 2" +(* ---- date-keyed windows (OLM n. 69.3) -- FIX 1, 2026-08-26 review ----- *) + +(* {!Lect_of.date_keyed_slug} itself, direct unit coverage of its own + boundaries: the O-Antiphon window (17-24 December), the Christmas-season + window (29-31 December, then 2-5 and 7 January), 6 January's own + deliberate exclusion (Temporal_of's own [named] fixes Epiphany there + unconditionally, so that date can never reach this function's caller as + a ferial), and the Sunday guard (a Sunday inside either window is a + NAMED office -- Advent 4, Holy Family, the Second Sunday after + Christmas -- never one of these ferial dates). *) +let test_date_keyed_slug_boundaries () = + let some_str dt = Option.map Slug.to_string (Lect_of.date_keyed_slug dt) in + Alcotest.(check (option string)) "16 December: outside the window" None (some_str (d 2026 12 16)); + Alcotest.(check (option string)) "17 December: in the window" (Some "of-advent-dec-17") (some_str (d 2026 12 17)); + Alcotest.(check (option string)) "24 December: in the window" (Some "of-advent-dec-24") (some_str (d 2029 12 24)); + Alcotest.(check (option string)) "25 December: outside the window" None (some_str (d 2026 12 25)); + Alcotest.(check (option string)) "28 December: outside the window" None (some_str (d 2026 12 28)); + Alcotest.(check (option string)) "29 December: in the window" (Some "of-christmas-dec-29") (some_str (d 2026 12 29)); + Alcotest.(check (option string)) "31 December: in the window" (Some "of-christmas-dec-31") (some_str (d 2029 12 31)); + Alcotest.(check (option string)) "1 January: outside the window" None (some_str (d 2026 1 1)); + Alcotest.(check (option string)) "2 January: in the window" (Some "of-christmas-jan-2") (some_str (d 2026 1 2)); + Alcotest.(check (option string)) "5 January: in the window" (Some "of-christmas-jan-5") (some_str (d 2026 1 5)); + Alcotest.(check (option string)) + "6 January: deliberately excluded, Epiphany is fixed there unconditionally" None (some_str (d 2026 1 6)); + Alcotest.(check (option string)) "7 January: in the window" (Some "of-christmas-jan-7") (some_str (d 2026 1 7)); + Alcotest.(check (option string)) "8 January: outside the window" None (some_str (d 2026 1 8)); + Alcotest.(check bool) "2028-12-24 is a Sunday, sanity check" true (D.weekday (d 2028 12 24) = D.Sun); + Alcotest.(check (option string)) "24 December on a Sunday (2028, Advent 4): guarded off" None + (some_str (d 2028 12 24)); + Alcotest.(check bool) "2025-01-05 is a Sunday, sanity check" true (D.weekday (d 2025 1 5) = D.Sun); + Alcotest.(check (option string)) "5 January on a Sunday (2025): guarded off" None (some_str (d 2025 1 5)) + +(* The fix's own headline claim, through the REAL resolution chain, not + just the slug function in isolation: the same civil date resolves the + SAME citation across two years with a different weekday alignment, + where it used to drift. Verified by hand against the pre-fix binary + before this fix landed: 2024-12-17 served "Zephaniah 3:1-2,9-13/ + Matth 21:28-32" (that year's own "of-advent-3-tuesday" content) while + 2029-12-17 served "Numbers 24:2-7,15-17a/Matth 21:23-27" ("of-advent-3- + monday") -- two DIFFERENT citations for the identical calendar date. + 2024 (Tuesday) and 2029 (Monday) are two different, non-Sunday + weekdays, so neither the sanctoral layer nor the Sunday guard is in + play -- both years reach {!Lect_of.readings}'s own step 3 through the + plain temporal-ferial branch, exactly like {!test_step3_temporal_ferial} + above. *) +let test_o_antiphon_no_drift () = + let check_year y expected_weekday_slug = + let lectionary = real_lectionary () in + let layer = real_sanctoral_layer () in + let index = L.index layer ~easter:Computus.gregorian_easter ~years:[ y - 1; y ] in + let date = d y 12 17 in + let expected_slug = (T.temporal date).Colitur_kernel.Temporal.office.Cel.slug in + Alcotest.(check string) (Printf.sprintf "%d-12-17: expected temporal slug" y) expected_weekday_slug + (Slug.to_string expected_slug); + match readings_on lectionary index date with + | Some fm, cs -> + Alcotest.(check bool) (Printf.sprintf "%d: tagged Own_slug" y) true + (fm.Mass_formulary.via = Mass_formulary.Own_slug); + Alcotest.(check (option string)) (Printf.sprintf "%d: said the temporal slug" y) + (Some (Slug.to_string expected_slug)) (Option.map Slug.to_string fm.Mass_formulary.said); + Alcotest.(check (list string)) (Printf.sprintf "%d: 17 December is Gen 49:2,8-10 / Matthew 1:1-17" y) + [ "Genesis 49:2,8-10"; "Matthew 1:1-17" ] + (List.map (fun (c : Colitur_kernel.Citation.t) -> c.reference) cs) + | None, _ -> Alcotest.failf "%d-12-17 should resolve readings" y + in + check_year 2024 "of-advent-3-tuesday"; + check_year 2029 "of-advent-3-monday" + +(* Same claim, the Christmas-season window, through a REALISTIC step 2 -> + step 3 path: 2 January carries the fixed memorial of Saints Basil and + Gregory Nazianzen (calendar-2002.sexp), which has no dedicated + lectionary entry of its own, so both years fall through to the day's + own ferial -- exactly the shape 2024-01-02's own CLI-verified pre-fix + drift took (that year served "1 John 3:22-4:6/Matt 4:12-17,23-25", + 2029's weekday-keyed content, before this fix). 2024 (Tuesday) and 2025 + (Thursday) are again two different non-Sunday weekdays. *) +let test_christmas_date_keyed_no_drift () = + let check_year y = + let lectionary = real_lectionary () in + let layer = real_sanctoral_layer () in + let index = L.index layer ~easter:Computus.gregorian_easter ~years:[ y - 1; y ] in + let date = d y 1 2 in + (match L.on_date index date with + | [ e ] -> + Alcotest.(check string) (Printf.sprintf "%d-01-02: Basil/Gregory's own entry" y) + "saints-basil-the-great-and-gregory-nazianzen-bishops-and-doctors" (Slug.to_string e.L.cel.Cel.slug) + | es -> Alcotest.failf "%d-01-02: expected exactly one sanctoral entry, got %d" y (List.length es)); + match readings_on lectionary index date with + | Some fm, cs -> + Alcotest.(check bool) (Printf.sprintf "%d: tagged Own_slug (fell through to the ferial)" y) true + (fm.Mass_formulary.via = Mass_formulary.Own_slug); + Alcotest.(check (list string)) (Printf.sprintf "%d: 2 January is 1 John 2:22-28 / John 1:19-28" y) + [ "1 John 2:22-28"; "John 1:19-28" ] + (List.map (fun (c : Colitur_kernel.Citation.t) -> c.reference) cs) + | None, _ -> Alcotest.failf "%d-01-02 should resolve readings" y + in + check_year 2024; + check_year 2025 + let suite = [ Alcotest.test_case "Sunday cycle table (OLM n.66, straddles Advent I)" `Quick test_sunday_cycle_table; Alcotest.test_case "weekday cycle table (OLM n.69.4, straddles Advent I)" `Quick test_weekday_cycle_table; @@ -364,9 +464,14 @@ let suite = Alcotest.test_case "data/of/lectionary.sexp SHA-256 pinned" `Quick test_sha256_pinned; Alcotest.test_case "lectio source SHA-256 pinned in the header" `Quick test_source_sha256_pinned_in_header; Alcotest.test_case "a missing lectio source fails loudly, not silently" `Quick test_missing_source_fails_loudly; - Alcotest.test_case "loads, 754 entries" `Quick test_load; + Alcotest.test_case "loads, 770 entries" `Quick test_load; Alcotest.test_case "2026 coverage: exactly {25 December} unresolved" `Quick test_coverage_2026; Alcotest.test_case "2026 coverage: 364 days resolve" `Quick test_pinned_resolved_count; Alcotest.test_case "step 3: temporal ferial fallback" `Quick test_step3_temporal_ferial; - Alcotest.test_case "step 2: sanctoral proper wins over the ferial" `Quick test_step2_sanctoral_wins + Alcotest.test_case "step 2: sanctoral proper wins over the ferial" `Quick test_step2_sanctoral_wins; + Alcotest.test_case "date_keyed_slug boundaries (OLM n. 69.3)" `Quick test_date_keyed_slug_boundaries; + Alcotest.test_case "O-Antiphon date-keyed reading does not drift (17 December)" `Quick + test_o_antiphon_no_drift; + Alcotest.test_case "Christmas-season date-keyed reading does not drift (2 January)" `Quick + test_christmas_date_keyed_no_drift ] |
