diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-13 20:15:42 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-13 20:15:42 +0200 |
| commit | 84333a8c74ddb3988ad88398632ca5e1c7788e56 (patch) | |
| tree | 08b998e72818752dad163343c98f70a78243e33e /test/test_rite_ef.ml | |
| parent | 3364e23fc49dc5b49576a97315326de030376e31 (diff) | |
| download | colitur-84333a8c74ddb3988ad88398632ca5e1c7788e56.tar.gz colitur-84333a8c74ddb3988ad88398632ca5e1c7788e56.zip | |
test(ef): commemoration, transfer, and privilege coverage for the Major Litanies
test_precedence_ef.ml: a privilege_cases row for RG109(f), deliberately
rank Class4 so it cannot pass via the pre-existing "of a I-class day"
branch by accident. Three disposition_cases rows: the two RG80 trigger
shapes (Transfer, not Commemorate) and a synthetic third row proving the
winner-slug guard is specific to the two named trigger days, not "any
I-class winner". Two transfer_target unit tests, both using a pathological
always-blocking occupant to prove no RG96 search runs at all for this
candidate -- a much stronger witness than a cooperative occupant, which
could pass by accident.
test_golden.ml: three new pinned tests through the real data pipeline --
the ordinary case (2026-04-25, matches the oracle's own M5 date), the four
Sunday-displacement years in 2005-2050 (2010, 2021, 2027, 2032, RG111(b)),
and the Easter-Monday transfer shape (2011, complementing the
Easter-Sunday shape already pinned by the existing 1666/2038 Easter-extreme
tests, now widened to include the transfer and its target).
test_rite_ef.ml: the existing "no transfer lands inside the Easter octave"
property gets a named, cited exemption for the Major Litanies (RG80's own
deliberate exception), plus a positive companion test asserting the
exemption is not vacuous (exactly two landings in 2005-2050, both
Easter+2). That companion test was written but never registered in its
own suite list -- caught while mutation-testing the transfer (a mutation
that should have failed it passed silently); fixed by registering it.
Mutation-tested, each of the three parts separately (recorded in the
task's own report): moving the data entity off 25 April fails 7 tests
(golden + oracle only -- zero unit tests, since they build synthetic
candidates); corrupting the transfer's winner-slug guard fails 6 (the two
disposition_cases rows, 3 golden pins, and the newly-registered
transfer-count test); flipping the privilege to Ordinary fails 9
(privilege_cases, the disposition SYNTHETIC row, the oracle's count pin,
5 golden pins, and the exhaustive Validate property sweep).
Diffstat (limited to 'test/test_rite_ef.ml')
| -rw-r--r-- | test/test_rite_ef.ml | 53 |
1 files changed, 50 insertions, 3 deletions
diff --git a/test/test_rite_ef.ml b/test/test_rite_ef.ml index 9a1e6d5..6cd8709 100644 --- a/test/test_rite_ef.ml +++ b/test/test_rite_ef.ml @@ -233,6 +233,21 @@ let sample_years = let rec range a b = if a > b then [] else a :: range (a + 1) b in range 2005 2050 +(* ef-major-litanies task: [major-litanies] (RG 80, Precedence_ef's own + [major_litanies_slug]) is a DELIBERATE, cited exception to this + property, not a bug to catch -- RG 80's own text sends the Major + Litanies to "the following Tuesday" unconditionally in a transfer year, + which is always Easter+2, squarely inside [Easter, Easter+7] + (precedence_ef.ml's own [transfer_target] Litanies branch has the full + citation and the "why not the general RG96 search" argument). Every + OTHER slug this rite ever transfers is still held to the original + property below: RG 96's general walk (and its own named Annunciation + exception) is what this property actually protects, and neither of + those two mechanisms is exempted here -- only this one rite-cited, + hand-verified exception, matched by NAME so a future regression in some + OTHER slug cannot silently hide behind this exemption. *) +let is_major_litanies_slug slug = String.equal slug PE.major_litanies_slug + (* Property 1: no day in the resolved output, across the whole sample, is EVER a transfer's landing point inside [Easter, Easter+7] -- checked two ways. [transferred_out]'s own recorded target is what [transfer_target] @@ -248,7 +263,7 @@ let test_no_transfer_lands_in_easter_octave () = Array.iter (fun (d : (V.season, V.rank) LD.t) -> (match d.LD.transferred_in with - | Some c when in_easter_octave d.LD.date -> + | Some c when in_easter_octave d.LD.date && not (is_major_litanies_slug (slug_of c)) -> violations := (Printf.sprintf "%s transferred_in on %s (Easter+%d)" (slug_of c) (Date.to_iso8601 d.LD.date) (easter_offset d.LD.date)) @@ -256,7 +271,7 @@ let test_no_transfer_lands_in_easter_octave () = | _ -> ()); List.iter (fun (c, target) -> - if in_easter_octave target then + if in_easter_octave target && not (is_major_litanies_slug (slug_of c)) then violations := Printf.sprintf "%s transferred_out from %s to %s (Easter+%d)" (slug_of c) (Date.to_iso8601 d.LD.date) (Date.to_iso8601 target) (easter_offset target) @@ -265,7 +280,37 @@ let test_no_transfer_lands_in_easter_octave () = days) sample_years; Alcotest.(check (list string)) - "no day in [Easter, Easter+7] is ever a transfer's target, 2005-2050" [] (List.rev !violations) + "no day (other than the RG80-cited Major Litanies exception) in [Easter, Easter+7] is ever a transfer's \ + target, 2005-2050" [] + (List.rev !violations) + +(* The positive contrast to the property above: RG 80's own exception DOES + fire, exactly twice in the 2005-2050 sample (register: 194/8417 domain- + wide trigger years; this task's own report has the full count), both + landing on Easter+2 as RG 80's text requires -- proving the exemption + above is not merely masking an absent case. 2011 (Easter Monday = 25 + April, Easter = 24 April) and 2038 (Easter Sunday = 25 April) are this + file's own worked examples (precedence_ef.ml's [major_litanies_slug] + citation). *) +let test_major_litanies_transfers_inside_easter_octave_exactly_when_rg80_requires () = + let layer = real_layer () in + let landings = ref [] in + List.iter + (fun y -> + let days = Cal.year Rite_ef.context layer y in + Array.iter + (fun (d : (V.season, V.rank) LD.t) -> + List.iter + (fun (c, target) -> + if is_major_litanies_slug (slug_of c) then + landings := (Date.to_iso8601 d.LD.date, Date.to_iso8601 target) :: !landings) + d.LD.transferred_out) + days) + sample_years; + Alcotest.(check (list (pair string string))) + "RG80 fires exactly twice in 2005-2050, both landing on Easter+2" + [ ("2011-04-25", "2011-04-26"); ("2038-04-25", "2038-04-27") ] + (List.rev !landings) (* Property 2, and the LIVE case: [PE.transfer_target] called directly, with an origin that genuinely starts the search INSIDE Holy Week -- Holy @@ -381,6 +426,8 @@ let suite = test_transfer_search_does_not_raise_at_domain_ceiling; Alcotest.test_case "no transfer ever lands inside [Easter, Easter+7], 2005-2050" `Quick test_no_transfer_lands_in_easter_octave; + Alcotest.test_case "RG80: the Major Litanies DO transfer inside the octave, exactly twice in 2005-2050" + `Quick test_major_litanies_transfers_inside_easter_octave_exactly_when_rg80_requires; Alcotest.test_case "transfer_target skips the whole Easter octave from inside Holy Week" `Quick test_transfer_target_skips_the_whole_easter_octave; Alcotest.test_case "the search genuinely enters the window (not vacuous)" `Quick |
