aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/test_precedence_ef.ml102
1 files changed, 96 insertions, 6 deletions
diff --git a/test/test_precedence_ef.ml b/test/test_precedence_ef.ml
index fb05582..7033978 100644
--- a/test/test_precedence_ef.ml
+++ b/test/test_precedence_ef.ml
@@ -30,6 +30,17 @@ let cand ?(origin = P.Temporal) ?(rank = V.Class1) ?(subject = Sub.Temporal) ?(l
{ P.cel = Cel.make ~slug:(S.of_string_exn slug) ~rank ~colour:Col.White ~subject ~layer ();
origin }
+(* A candidate built from [Temporal_ef.temporal]'s own real output, not a
+ hand-typed slug -- review finding 3: [band]'s Ember/vigil detection reads
+ temporal_ef.ml's slug conventions, and a row that also hand-types the same
+ literal proves nothing if that convention ever drifts (both sides would
+ drift together, silently). Rows built with this instead fail loudly on
+ such a drift, because they source the slug from the same place [band]
+ itself is implicitly trusting. *)
+let of_temporal date =
+ let day = T.temporal date in
+ { P.cel = day.Colitur_kernel.Temporal.office; origin = P.Temporal }
+
(* Every Easter-relative date below is anchored to this single computed
Easter rather than a hand-typed calendar date, so an arithmetic slip in a
test date cannot silently pass by accident. *)
@@ -76,8 +87,20 @@ let cases =
("7 Monday of Holy Week", off (-6), cand "ef-holy-monday", 7);
("7 Tuesday of Holy Week", off (-5), cand "ef-holy-tuesday", 7);
("7 Wednesday of Holy Week", off (-4), cand "ef-holy-wednesday", 7);
- (* Entry 8 -- register line 334. *)
- ("8 All Souls", mk 2026 11 2, cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls", 8);
+ (* Entry 8 -- register line 334. 2 Nov 2026 is a Monday (verified
+ independently below the table), so this row is the plain case. The
+ register's own qualifying case -- "yields to an occurring Sunday" --
+ gets its own row and its own end-to-end test after this table (2 Nov
+ 2025 is a real Sunday). *)
+ ("8 All Souls (non-Sunday)", mk 2026 11 2, cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls", 8);
+ (* Entry 8's qualifier: "(yields to an occurring Sunday)". 2 Nov 2025 is
+ a Sunday, so this must NOT be 8 -- it must lose to entry 15 (16 =
+ entry 15's own value + 1, the exact value precedence_ef.ml documents
+ and justifies at entry 8's branch). The end-to-end resolve-level
+ proof that the Sunday actually wins the day is
+ [test_all_souls_yields_to_sunday] below; this row pins the specific
+ integer [band] returns. *)
+ ("8 All Souls (yields to a Sunday, 2 Nov 2025)", mk 2025 11 2, cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls", 16);
(* Entry 9 -- register line 335. *)
("9 Pentecost Vigil", off 48, cand "ef-pentecost-vigil", 9);
(* Entry 10 -- register line 336: both range boundaries, to guard the
@@ -101,11 +124,20 @@ let cases =
cand ~origin:P.Sanctoral ~subject:Sub.Saint ~layer:(PE.indult_prefix ^ "local-grant")
"ef-indult-feast-1",
13 );
- (* Entry 14 -- register line 341. *)
+ (* Entry 14 -- register line 341, deliberately UNQUALIFIED (contrast
+ entry 16, line 342, which explicitly says "not of the Lord"). *)
( "14 Feast of the Lord, II class", mk 2026 7 1,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer
"ef-precious-blood",
14 );
+ (* Entry 14, non-base layer: unlike 11-13/16/19/20/23/24, entry 14 draws
+ no universal/proper/indult line at all, so this must STILL be 14, not
+ 19 -- the exact restriction review finding 2 flagged and this row
+ exists to keep from silently coming back. *)
+ ( "14 Feast of the Lord, II class (non-base layer)", mk 2026 7 2,
+ cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:"diocese-warsaw"
+ "ef-local-feast-of-the-lord",
+ 14 );
(* Entry 15 -- register line 342: an ordinary Sunday not named at entry 6
-- Septuagesima is II class (RG 11-12 names only Advent/Lent/
Passiontide/Easter/Low/Pentecost as I class). *)
@@ -128,7 +160,10 @@ let cases =
about, worked the other way round: this Ember day must NOT be
mistaken for an ordinary entry-22 Lent feria either. *)
("18 Advent 17-23 Dec feria", mk 2026 12 21, cand ~rank:V.Class2 "ef-advent-4-mon", 18);
- ("18 Lent Ember Wednesday", off (-39), cand ~rank:V.Class2 "ef-lent-ember-wed", 18);
+ (* Sourced from Temporal_ef.temporal's own output (see [of_temporal])
+ rather than a hand-typed "ef-lent-ember-wed" -- closes review finding
+ 3's coupling concern for the Ember prefixes specifically. *)
+ ("18 Lent Ember Wednesday (from Temporal_ef.temporal)", off (-39), of_temporal (off (-39)), 18);
(* Entry 19 -- register line 344. *)
( "19 Proper II-class feast", mk 2026 1 20,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Saint ~layer:"diocese-warsaw"
@@ -145,10 +180,20 @@ let cases =
sanctoral-origin case no task has loaded data for yet -- proving
[band] does not gate this entry on [origin] (see precedence_ef.ml's
file comment). *)
- ("21 Ascension Vigil (temporal-origin)", off 38, cand ~rank:V.Class2 "ef-ascension-vigil", 21);
+ (* Sourced from Temporal_ef.temporal's own output (see [of_temporal])
+ rather than a hand-typed "ef-ascension-vigil" -- closes review finding
+ 3's coupling concern for [vigil_suffix]. *)
+ ("21 Ascension Vigil (from Temporal_ef.temporal)", off 38, of_temporal (off 38), 21);
( "21 Assumption Vigil (sanctoral-origin)", mk 2026 8 14,
cand ~origin:P.Sanctoral ~rank:V.Class2 ~layer:PE.universal_layer "ef-assumption-vigil",
21 );
+ (* Also review finding 3 / "worth doing": a UNIVERSAL-layer Class2 vigil
+ whose subject is the Lord must still be 21, not 14 -- pins entry 14's
+ [not is_vigil] guard even after finding 2 dropped its layer test. *)
+ ( "21 Universal II-class vigil of the Lord", mk 2026 6 23,
+ cand ~origin:P.Sanctoral ~rank:V.Class2 ~subject:Sub.Lord ~layer:PE.universal_layer
+ "ef-precious-blood-vigil",
+ 21 );
(* Entry 22 -- register line 347-348 (corrected: ends at Palm Sunday, not
Passion Sunday). Both a Lent and a Passiontide feria, clear of Ash
Wednesday, Holy Week and the Ember days. *)
@@ -172,6 +217,11 @@ let cases =
( "26 III-class vigil", mk 2026 8 9,
cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:PE.universal_layer "ef-lawrence-vigil",
26 );
+ (* Also worth doing: a NON-universal-layer Class3 vigil must still be 26,
+ not 23 -- pins entry 23's [not is_vigil] guard. *)
+ ( "26 III-class vigil (non-base layer)", mk 2026 8 10,
+ cand ~origin:P.Sanctoral ~rank:V.Class3 ~layer:"diocese-warsaw" "ef-local-patron-vigil",
+ 26 );
(* Entry 27 -- register line 352: an otherwise-unoccupied IV-class
Saturday. *)
( "27 Office of the BVM on Saturday", off 62,
@@ -187,16 +237,56 @@ let cases =
( "unclassified: I-class vigil outside Nativity/Pentecost", mk 2026 3 10,
cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-mystery-vigil",
PE.unclassified );
+ (* Also worth doing: a temporal-origin Class1 candidate on a date none of
+ entries 1/2/3/5/6/7/9/10 name. 15 Jul 2026 is a Wednesday, off=101
+ from Easter -- clear of every Easter-relative window this module
+ checks, and not one of the fixed dates either. Without the
+ [not is_temporal] guard on entries 11-13, this would wrongly reach 12
+ (its default layer, "temporal", is not [universal_layer] and does not
+ carry [indult_prefix], so it reads as "proper" by the layer test
+ alone -- precisely the bug the guard exists to prevent; see the
+ [not is_temporal] guard's role in the entry-25 mutation test recorded
+ in the task report). *)
+ ("unclassified: I-class temporal candidate on an unnamed date", mk 2026 7 15, cand "ef-unnamed-day", PE.unclassified);
(* RG 91's own vigil list (register lines 381-384) stops at III class --
there is no IV-class vigil for entry 28's ferial catch-all to absorb. *)
( "unclassified: IV-class candidate marked as a vigil", mk 2026 6 20,
cand ~rank:V.Class4 "ef-second-mystery-vigil", PE.unclassified )
]
+(* Review finding 1's end-to-end proof: on a real Sunday landing on 2
+ November, [Precedence.resolve] -- not just [band] in isolation -- observes
+ the Sunday, not All Souls. This exercises the exact mechanism the finding
+ named ("resolve observes the lowest band, so whenever 2 November falls on
+ a Sunday, All Souls wins and the Sunday loses"), rather than only the
+ integer [band] returns for the standalone row above. [disposition] and
+ [admit] are stubs -- only [observed] is under test here. *)
+let test_all_souls_yields_to_sunday () =
+ let date = mk 2025 11 2 in
+ let day_ctx = ctx date in
+ let sunday =
+ { P.cel =
+ Cel.make ~slug:(S.of_string_exn "ef-time-after-pentecost-sunday-x") ~rank:V.Class2
+ ~colour:Col.Green ~subject:Sub.Temporal ~layer:"temporal" ();
+ origin = P.Temporal }
+ in
+ let all_souls = cand ~origin:P.Sanctoral ~layer:PE.universal_layer "ef-all-souls" in
+ let rules =
+ { P.band = (fun c cd -> PE.band c cd);
+ disposition = (fun ~winner:_ ~loser:_ -> P.Omit);
+ admit = (fun ~observed:_ cs -> cs) }
+ in
+ let resolution = P.resolve rules day_ctx ~temporal:sunday ~sanctoral:[ all_souls ] in
+ Alcotest.(check string) "the Sunday is observed, not All Souls"
+ "ef-time-after-pentecost-sunday-x"
+ (S.to_string resolution.P.observed.P.cel.Cel.slug)
+
let suite =
( "Precedence_ef",
List.map
(fun (desc, date, c, expect) ->
Alcotest.test_case desc `Quick (fun () ->
Alcotest.(check int) desc expect (PE.band (ctx date) c)))
- cases )
+ cases
+ @ [ Alcotest.test_case "8 All Souls yields to a Sunday (resolve-level)" `Quick
+ test_all_souls_yields_to_sunday ] )