aboutsummaryrefslogtreecommitdiff
path: root/test/test_temporal_ef.ml
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_temporal_ef.ml')
-rw-r--r--test/test_temporal_ef.ml156
1 files changed, 156 insertions, 0 deletions
diff --git a/test/test_temporal_ef.ml b/test/test_temporal_ef.ml
index 23a817d..f341533 100644
--- a/test/test_temporal_ef.ml
+++ b/test/test_temporal_ef.ml
@@ -272,6 +272,105 @@ let test_holy_family_no_gap_year () =
Alcotest.(check string) "2025 (6 Jan a Monday): 12 Jan" "2025-01-12"
(D.to_iso8601 (T.holy_family_sunday 2025))
+(* RG 17(a) (Caput III, "De Dominicis"; full text on [T.holy_name_sunday]'s
+ own citation), Sunday shape: "festum Ss.mi Nominis Iesu, celebrandum
+ dominica quae occurrit a die 2 ad 5 ianuarii". Deliberately NOT part of
+ [T.named], same reason as Holy Family (that test's own comment): the
+ slug/rank/colour this branch produces for a plain Sunday 2-5 January
+ already match what [T.sunday_slug]'s Christmastide branch always
+ computed; only [subject]/[names] change. *)
+let test_holy_name_sunday_shape () =
+ (* 2026: 4 January is the Sunday (independently cross-checked against
+ `date -d 2026-01-04 +%u` = 7). Oracle-corroborated: missalemeum's own
+ 2026-01-04 row is rank 2, white, title "Holy Name of Jesus" (the task
+ brief's own example). *)
+ Alcotest.(check string) "2026: slug is the ordinary Holy-Name-window Sunday key" "ef-holy-name-sunday"
+ (slug_of (d 2026 1 4));
+ Alcotest.(check string) "2026: II class" "class-2" (rank_of (d 2026 1 4));
+ Alcotest.(check string) "2026: white" "white" (colour_of (d 2026 1 4));
+ Alcotest.(check bool) "2026: subject is Lord (RG 91 entry 14, \"Festa Domini II classis\")" true
+ (subject_of (d 2026 1 4) = Sub.Lord);
+ Alcotest.(check (option string)) "2026: Latin name from the calendarium/propers, verbatim"
+ (Some "Sanctissimi Nominis Iesu") (name_la_of (d 2026 1 4));
+ Alcotest.(check (option string)) "2026: no English name (deliberate -- would read the oracle it is \
+ compared against)"
+ None (Colitur_kernel.Names.find (office (d 2026 1 4)).Cel.names en);
+ (* Negative controls: every OTHER day in the 2-5 January window that year
+ stays [subject Temporal] -- the override is scoped to exactly the one
+ Sunday [T.holy_name_sunday] names, not the whole window. *)
+ List.iter
+ (fun dd ->
+ Alcotest.(check bool) (Printf.sprintf "2026-01-%02d: NOT Lord (not the Sunday)" dd) true
+ (subject_of (d 2026 1 dd) = Sub.Temporal))
+ [ 2; 3; 5 ];
+ (* 2027: 3 January is the Sunday (`date -d 2027-01-03 +%u` = 7) -- the
+ oracle window's own second instance of the Sunday shape. missalemeum's
+ 2027-01-03 row independently corroborates: rank 2, white, title "Holy
+ Name of Jesus". *)
+ Alcotest.(check bool) "2027: 3 January is a Sunday" true (D.weekday (d 2027 1 3) = D.Sun);
+ Alcotest.(check string) "2027: same slug" "ef-holy-name-sunday" (slug_of (d 2027 1 3));
+ Alcotest.(check bool) "2027: still subject Lord" true (subject_of (d 2027 1 3) = Sub.Lord);
+ (* Cross-checked against [T.holy_name_sunday] itself, independent of the
+ literal dates above. *)
+ Alcotest.(check (option string)) "2026: T.holy_name_sunday agrees" (Some "2026-01-04")
+ (Option.map D.to_iso8601 (T.holy_name_sunday 2026));
+ Alcotest.(check (option string)) "2027: T.holy_name_sunday agrees" (Some "2027-01-03")
+ (Option.map D.to_iso8601 (T.holy_name_sunday 2027))
+
+(* RG 17(a)'s own fallback clause, "(secus die 2 ianuarii)" -- the calendarium
+ confirms it in its own words: "vel, ea deficiente, die 2 ianuarii:
+ Sanctissimi Nominis Iesu, II classis". Unlike Holy Family's 7-13 January
+ window, this narrower 2-5 January window CAN be empty of a Sunday --
+ 3,619 of the 8,417 years in [1583, 9999] -- and colitur emitted NO Holy
+ Name office at all on any of them before this fix (register ยง6.2's own
+ account of the gap this closes). *)
+let test_holy_name_fallback_shape () =
+ (* 2029: no Sunday falls 2-5 January (independently cross-checked: `date -d
+ 2029-01-0{2,3,4,5} +%u` are 2,3,4,5 -- Tuesday through Friday, none a
+ 7). The task brief's own reported example: "2029-01-02
+ ef-christmas-1-tuesday class-4 white (no Sunday in window: feast
+ MISSING)" -- that is the pre-fix behaviour this test pins the fix
+ against. *)
+ Alcotest.(check (option string)) "2029: no Sunday in the window" None
+ (Option.map D.to_iso8601 (T.holy_name_sunday 2029));
+ Alcotest.(check string) "2029: slug is the fallback key" "ef-holy-name" (slug_of (d 2029 1 2));
+ Alcotest.(check string) "2029: II class" "class-2" (rank_of (d 2029 1 2));
+ Alcotest.(check string) "2029: white" "white" (colour_of (d 2029 1 2));
+ Alcotest.(check bool) "2029: subject is Lord, same as the Sunday shape" true
+ (subject_of (d 2029 1 2) = Sub.Lord);
+ Alcotest.(check (option string)) "2029: SAME Latin name as the Sunday shape (one feast, two dates)"
+ (Some "Sanctissimi Nominis Iesu") (name_la_of (d 2029 1 2));
+ (* Negative controls: 3, 4 and 5 January stay ordinary Christmastide
+ ferias in a fallback year -- the fallback names 2 January specifically,
+ not "some day near the start of the window". *)
+ List.iter
+ (fun dd ->
+ Alcotest.(check bool) (Printf.sprintf "2029-01-%02d: NOT the feast (ordinary feria)" dd) true
+ (subject_of (d 2029 1 dd) = Sub.Temporal
+ && Sl.to_string (office (d 2029 1 dd)).Cel.slug <> "ef-holy-name"))
+ [ 3; 4; 5 ];
+ (* A second, independently cross-checked fallback year, at the OTHER end
+ of the week (2008: `date -d 2008-01-02 +%A` = Wednesday), so this is
+ not a single-year accident. *)
+ Alcotest.(check (option string)) "2008: no Sunday in the window either" None
+ (Option.map D.to_iso8601 (T.holy_name_sunday 2008));
+ Alcotest.(check string) "2008: same fallback slug" "ef-holy-name" (slug_of (d 2008 1 2))
+
+(* [T.holy_name_sunday]'s own [Some]/[None] split, cross-checked against
+ `date -d` independently for a sample spanning both shapes -- not merely
+ the two years the shape tests above already pin literally, so the
+ boundary itself (not only two hand-picked instances) is exercised. *)
+let test_holy_name_sunday_or_none () =
+ List.iter
+ (fun (y, expected) ->
+ Alcotest.(check (option string))
+ (Printf.sprintf "%d: holy_name_sunday" y)
+ expected
+ (Option.map D.to_iso8601 (T.holy_name_sunday y)))
+ [ (2026, Some "2026-01-04"); (2027, Some "2027-01-03"); (2009, Some "2009-01-04");
+ (2010, Some "2010-01-03"); (2020, Some "2020-01-05"); (2025, Some "2025-01-05");
+ (2006, None); (2007, None); (2008, None); (2018, None); (2029, None) ]
+
(* Regression for a Task 14 Validate finding, reworked (register finding 4):
[named] no longer carries its own week at all -- [temporal] computes it
uniformly via [week] for every day, named or not -- so "a named day inside
@@ -583,6 +682,56 @@ let test_holy_family_anchor_present_and_erosion_is_caught () =
Alcotest.(check string) "sanity: temporal itself is unaffected by the anchors-list erosion"
expected_slug (slug_of expected_date)
+(* Holy Name of Jesus (RG 17(a)), SUNDAY shape -- same structure as Holy
+ Family's own test immediately above, same reasoning (also not one of
+ [T.named]'s outputs). 2026 is a Sunday-shape year (4 January). *)
+let test_holy_name_sunday_anchor_present_and_erosion_is_caught () =
+ let y = 2026 in
+ let expected_date =
+ match T.holy_name_sunday y with
+ | Some d -> d
+ | None -> Alcotest.fail "sanity: 2026 must have a Sunday in the Holy Name window"
+ in
+ let expected_slug =
+ match T.sunday_slug expected_date with
+ | Some s -> s
+ | None -> Alcotest.fail "sanity: holy_name_sunday must itself be a Sunday"
+ in
+ Alcotest.(check (list string))
+ "the anchor entry exists: T.sunday_slug's own independent computation is not missing from T.anchors" []
+ (missing_from_anchors ~named_slugs:[ expected_slug ] ~anchors:(anchor_slugs y));
+ let eroded_anchors = List.filter (fun s -> s <> expected_slug) (anchor_slugs y) in
+ Alcotest.(check (list string)) "the erosion is caught: missing_from_anchors reports it, and only it"
+ [ expected_slug ]
+ (missing_from_anchors ~named_slugs:[ expected_slug ] ~anchors:eroded_anchors);
+ Alcotest.(check string) "sanity: temporal itself is unaffected by the anchors-list erosion"
+ expected_slug (slug_of expected_date)
+
+(* Holy Name of Jesus, FALLBACK shape -- the SAME two properties (presence,
+ erosion caught), for a year with no Sunday in the window. Unlike the
+ Sunday shape above, there is no SECOND independent function (the way
+ [T.sunday_slug] is independent of [T.anchors]) that names "ef-holy-name"
+ for a non-Sunday date -- [expected_slug] is the literal this task chose,
+ cross-checked instead against [T.temporal]'s own output (the function
+ under test) via the final sanity line, same as every other branch of
+ this cross-check, honestly not a second independent source. 2029 has no
+ Sunday 2-5 January (test_holy_name_fallback_shape's own citation). *)
+let test_holy_name_fallback_anchor_present_and_erosion_is_caught () =
+ let y = 2029 in
+ (match T.holy_name_sunday y with
+ | None -> ()
+ | Some _ -> Alcotest.fail "sanity: 2029 must have no Sunday in the Holy Name window");
+ let expected_date = T.holy_name_fallback_date y in
+ let expected_slug = "ef-holy-name" in
+ Alcotest.(check (list string)) "the anchor entry exists for the fallback shape" []
+ (missing_from_anchors ~named_slugs:[ expected_slug ] ~anchors:(anchor_slugs y));
+ let eroded_anchors = List.filter (fun s -> s <> expected_slug) (anchor_slugs y) in
+ Alcotest.(check (list string)) "the erosion is caught: missing_from_anchors reports it, and only it"
+ [ expected_slug ]
+ (missing_from_anchors ~named_slugs:[ expected_slug ] ~anchors:eroded_anchors);
+ Alcotest.(check string) "sanity: temporal itself is unaffected by the anchors-list erosion"
+ expected_slug (slug_of expected_date)
+
let test_totality () =
(* Every day of 2026 yields an office without raising. Not a slug
re-validation -- Slug.t is a private string validated on construction,
@@ -606,6 +755,9 @@ let suite_extra =
Alcotest.test_case "sunday slugs" `Quick test_sunday_slugs;
Alcotest.test_case "holy family (RG 17(b))" `Quick test_holy_family;
Alcotest.test_case "holy family: no gap year (RG 17(b))" `Quick test_holy_family_no_gap_year;
+ Alcotest.test_case "holy name: Sunday shape (RG 17(a))" `Quick test_holy_name_sunday_shape;
+ Alcotest.test_case "holy name: fallback shape (RG 17(a))" `Quick test_holy_name_fallback_shape;
+ Alcotest.test_case "holy name: Sunday-or-none cross-check" `Quick test_holy_name_sunday_or_none;
Alcotest.test_case "week/sunday_slug agree" `Quick test_week_sunday_slug_agree;
Alcotest.test_case "resumed sundays" `Quick test_resumed_sundays;
Alcotest.test_case "ferial slugs and ranks" `Quick test_ferial_slugs_and_ranks;
@@ -620,6 +772,10 @@ let suite_extra =
test_anchors_cover_all_named_days;
Alcotest.test_case "holy family anchor present, erosion caught" `Quick
test_holy_family_anchor_present_and_erosion_is_caught;
+ Alcotest.test_case "holy name (Sunday shape) anchor present, erosion caught" `Quick
+ test_holy_name_sunday_anchor_present_and_erosion_is_caught;
+ Alcotest.test_case "holy name (fallback shape) anchor present, erosion caught" `Quick
+ test_holy_name_fallback_anchor_present_and_erosion_is_caught;
Alcotest.test_case "anchors erosion is caught" `Quick test_anchors_erosion_is_caught ]
let suite =