summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-25 20:24:21 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-25 20:24:29 +0200
commite611e90b0b5433ddc9afdcf26cbf98a5c3a7be28 (patch)
treea6b49a21843078f965b330461108f6eddecb02a2 /test
parentce67fad69398047739a6ce89fb76f32b136b69f5 (diff)
downloadcolitur-e611e90b0b5433ddc9afdcf26cbf98a5c3a7be28.tar.gz
colitur-e611e90b0b5433ddc9afdcf26cbf98a5c3a7be28.zip
feat(of): transfer_target per Normae nn. 5 and 60
Three rules, in priority order: the Annunciation in Holy Week goes to Easter+8; any other solemnity on a Sunday of Advent, Lent or Easter goes to the following Monday (n. 5, which n. 60 defers to explicitly), except on Palm Sunday or Easter Sunday; otherwise the nearest later day free of Tabula entries 1-8. The n. 5 Monday rule is easy to miss -- n. 60 names it only by cross reference, and reading n. 60 alone yields a single search rule where the sources give three. Shaped to match Rite.t.transfer_target so a later phase wires it unchanged, with its own step bound: Calendar's round guard bounds rounds, not one call's internal search.
Diffstat (limited to 'test')
-rw-r--r--test/test_precedence_of.ml78
1 files changed, 77 insertions, 1 deletions
diff --git a/test/test_precedence_of.ml b/test/test_precedence_of.ml
index 6c50fa9..db217e8 100644
--- a/test/test_precedence_of.ml
+++ b/test/test_precedence_of.ml
@@ -337,6 +337,72 @@ let test_vigil_feast_is_always_none () =
(Rite_of.Precedence_of.vigil_feast c = None))
[ "of-nativity-vigil"; "of-nativity"; "of-pentecost"; "of-peter-and-paul" ]
+(* An [occupant] callback of the shape Rite.t.transfer_target expects, backed
+ by the real temporal cycle -- so the search meets genuine Tabula entries
+ rather than a hand-made fiction. *)
+let occupant d = (Rite_of.Temporal_of.temporal d).Temporal.office
+
+let annunciation =
+ mk ~subject:Subject.Lord ~slug:"of-annunciation" ~rank:Vocab_of.Sollemnitas
+ ~origin:Precedence.Sanctoral ()
+
+let iso s = Date.of_iso8601 s |> Result.get_ok
+
+let test_annunciation_in_holy_week () =
+ (* 2027: Easter is 28 March, so 25 March is Holy Thursday -- inside Holy
+ Week. Normae n. 60 sends it to the Monday after the Second Sunday of
+ Easter, Easter + 8 = 5 April 2027. *)
+ let target =
+ Rite_of.Precedence_of.transfer_target annunciation (iso "2027-03-25") occupant in
+ Alcotest.(check string) "Annunciation 2027 goes to Easter+8"
+ "2027-04-05" (Date.to_iso8601 target)
+
+let test_annunciation_in_easter_octave () =
+ (* 2035: Easter is 25 March, so the Annunciation collides with Easter Sunday
+ itself -- NOT Holy Week, so n. 60's Annunciation clause does not fire and
+ the general search runs. Every Octave day is Tabula entry 2, so the first
+ day free of entries 1-8 is the Monday after the Second Sunday of Easter:
+ the same 2 April the explicit rule would have named. Asserted rather than
+ assumed. *)
+ let target =
+ Rite_of.Precedence_of.transfer_target annunciation (iso "2035-03-25") occupant in
+ Alcotest.(check string) "Annunciation 2035 also lands on Easter+8"
+ "2035-04-02" (Date.to_iso8601 target)
+
+let test_solemnity_on_a_lenten_sunday () =
+ (* Normae n. 5: a solemnity occurring on a Sunday of Advent, Lent or Easter
+ goes to the FOLLOWING MONDAY, not to a searched free day. 19 March 2028
+ (St Joseph) is the Third Sunday of Lent. *)
+ let joseph =
+ mk ~slug:"of-joseph" ~rank:Vocab_of.Sollemnitas ~origin:Precedence.Sanctoral () in
+ let target =
+ Rite_of.Precedence_of.transfer_target joseph (iso "2028-03-19") occupant in
+ Alcotest.(check string) "St Joseph 2028 goes to the following Monday"
+ "2028-03-20" (Date.to_iso8601 target)
+
+let test_target_is_strictly_later () =
+ (* Rite.t.transfer_target's own stated obligation: Calendar treats
+ target <= origin as a legitimate placement, so a rule that can stand
+ still would loop candidates in place rather than fail loudly. *)
+ List.iter
+ (fun s ->
+ let d = iso s in
+ let t = Rite_of.Precedence_of.transfer_target annunciation d occupant in
+ Alcotest.(check bool)
+ (Printf.sprintf "%s -> strictly later" s)
+ true (Date.compare t d > 0))
+ [ "2027-03-25"; "2035-03-25"; "2026-03-25"; "2029-03-25"; "2032-03-25" ]
+
+let test_target_is_free_of_entries_1_to_8 () =
+ List.iter
+ (fun s ->
+ let t = Rite_of.Precedence_of.transfer_target annunciation (iso s) occupant in
+ let b = band_of_date t in
+ Alcotest.(check bool)
+ (Printf.sprintf "%s -> a day free of Tabula entries 1-8 (band %d)" s b)
+ true (b > 80))
+ [ "2027-03-25"; "2035-03-25"; "2026-03-25"; "2029-03-25" ]
+
let suite =
( "precedence-of",
[ Alcotest.test_case "Tabula part I (entries 1-4)" `Quick test_part1;
@@ -346,5 +412,15 @@ let suite =
Alcotest.test_case "disposition (Normae n. 60)" `Quick test_disposition;
Alcotest.test_case "disposition never commemorates" `Quick test_disposition_never_commemorates;
Alcotest.test_case "admit is always empty" `Quick test_admit_is_always_empty;
- Alcotest.test_case "vigil_feast is always none" `Quick test_vigil_feast_is_always_none ]
+ Alcotest.test_case "vigil_feast is always none" `Quick test_vigil_feast_is_always_none;
+ Alcotest.test_case "Annunciation in Holy Week -> Easter+8" `Quick
+ test_annunciation_in_holy_week;
+ Alcotest.test_case "Annunciation in the Easter Octave -> Easter+8 too" `Quick
+ test_annunciation_in_easter_octave;
+ Alcotest.test_case "solemnity on a Lenten Sunday -> following Monday" `Quick
+ test_solemnity_on_a_lenten_sunday;
+ Alcotest.test_case "transfer_target is always strictly later" `Quick
+ test_target_is_strictly_later;
+ Alcotest.test_case "transfer_target lands free of Tabula entries 1-8" `Quick
+ test_target_is_free_of_entries_1_to_8 ]
@ List.map QCheck_alcotest.to_alcotest [ prop_band_total ] )